﻿using FunnyArts.JSAdapter.Attri;
using FunnyArts.JSAdapter.Inter;
using FunnyArts.JSAdapter.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace FunnyArts.JSAdapter.Server
{
    internal class PrettyJSObjectReference
    {
        /// <summary>
        /// 创建前端调用的js对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="interClass"></param>
        internal List<JavaScriptObject> CreateJSObject<T>(T interClass)
            where T : class, IJavaScriptObject
        {
            List<JavaScriptObject> result= new List<JavaScriptObject>();
            Type type = interClass.GetType();
            var jSObject= type.GetCustomAttribute<JSObjectAttribute>();
            if (jSObject == null) return result;
            var methodInfoList=type.GetMethods().Where(w=>w.GetCustomAttribute<JSFunctionAttribute>() != null).ToList();
            if(methodInfoList==null|| methodInfoList.Count==0) return result;
            JavaScriptObject javaScriptObject = new JavaScriptObject()
            {
                ClassName = jSObject?.Name??type.Name,
                Methods = new List<JavaScriptMethod>(),
            };
            foreach (var item in methodInfoList)
            {
                //参数只有一个且为字符串，返回类型为字符串
                if (item.ReturnType != typeof(string)) continue;
                JavaScriptMethod javaScriptMethod = new JavaScriptMethod()
                {
                    Name = item.Name,
                    Params = new List<JavaScriptMethodParam>(),
                };
                var parameters=item.GetParameters();

                foreach (var parameter in parameters)
                {
                    javaScriptMethod.Params.Add(new JavaScriptMethodParam() { Name = parameter.Name });
                }
                javaScriptObject.Methods.Add(javaScriptMethod);
            }
            result.Add(javaScriptObject);
            return result;
        }

        internal void CreateJSFile(List<JavaScriptObject> javaScriptObjectList)
        {
            var jsDict= javaScriptObjectList.ToDictionary(x=>x.ClassName,y=> javaScriptObjectList.Where(s=>s.ClassName==y.ClassName));
            FileStream fileStream = default;
            if (File.Exists("JavaScriptAdapter.js"))
                File.Delete("JavaScriptAdapter.js");
            using (StreamWriter writer = new StreamWriter("JavaScriptAdapter.js"))
            {
                List<string> classWords = new List<string>();
                string header = "window.Api = {";
                string ender = "}";
                writer.WriteLine(header);
                foreach (var javaScriptObject in javaScriptObjectList)
                {
                    string classWord=string.Empty;
                    classWord += javaScriptObject.ClassName + ":{";
                    //writer.WriteLine(javaScriptObject.ClassName + ":{");
                    List<string> methodWords = new List<string>();
                    foreach (var mothed in javaScriptObject.Methods)
                    {
                        string word = "" + mothed.Name + "("+string.Join(',', mothed.Params.Select(s => s.Name)) +"){"
                        + "var resultStr=window.chrome.webview.hostObjects.sync." + javaScriptObject.ClassName+"."+ mothed.Name + "("+string.Join(',', mothed.Params.Select(s=>s.Name)) +");"
                        + "return resultStr;"
                        +"}";
                        methodWords.Add(word);
                    }
                    classWord+=string.Join(',', methodWords);
                    //writer.Write(string.Join(',', methodWords));
                    //writer.WriteLine("}");
                    classWord+="}";
                    classWords.Add(classWord);
                }
                writer.WriteLine(string.Join(',', classWords));
                writer.WriteLine(ender);
            }
        }

        internal async Task<string> CreateAndReadJSFile(List<JavaScriptObject> javaScriptObjectList)
        {
            CreateJSFile(javaScriptObjectList);
            using (StreamReader reader = new StreamReader("JavaScriptAdapter.js"))
            {
                return await reader.ReadToEndAsync();
            }
        }
    }

    public static class PrettyJSObjectReferenceExtend
    {
        internal static PrettyJSObjectReference prettyJSObjectReference  = new PrettyJSObjectReference();
        public static async Task<string> CreateAndReadJSFile<T>(this List<T> interClassList)
            where T : class, IJavaScriptObject
        {
            List < JavaScriptObject > javaScriptObjectList=new List<JavaScriptObject> ();
            foreach (var item in interClassList)
            {
                var javaScriptObjects=prettyJSObjectReference.CreateJSObject<T>(item);
                if (javaScriptObjects != null && javaScriptObjects.Count > 0)
                    javaScriptObjectList.AddRange(javaScriptObjects);
            }
            if (javaScriptObjectList.Count > 0)
                return await prettyJSObjectReference.CreateAndReadJSFile(javaScriptObjectList);
            else
                return string.Empty;
        }
    }
}
